package com.scs.application.modules.wm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UtilJava8;
import com.scs.application.modules.base.entity.Matr;
import com.scs.application.modules.base.entity.Warehouse;
import com.scs.application.modules.base.mapper.WarehouseMapper;
import com.scs.application.modules.base.service.MatrService;
import com.scs.application.modules.base.util.CommonService;
import com.scs.application.modules.wm.dto.LockMatrDTO;
import com.scs.application.modules.wm.entity.Stock;
import com.scs.application.modules.wm.entity.StockLock;
import com.scs.application.modules.wm.entity.StockLockLog;
import com.scs.application.modules.wm.enums.LockBusType;
import com.scs.application.modules.wm.enums.StockLockType;
import com.scs.application.modules.wm.enums.UniqueCodeType;
import com.scs.application.modules.wm.mapper.StockLockMapper;
import com.scs.application.modules.wm.service.StockLockLogService;
import com.scs.application.modules.wm.service.StockLockService;
import com.scs.application.modules.wm.service.StockService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 库存锁定 服务实现类
 * </p>
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class StockLockServiceImpl extends BaseServiceImpl<StockLockMapper, StockLock> implements StockLockService {
    @Autowired
    StockService stockService;
    @Autowired
    @Lazy
    MatrService matrService;
    @Autowired
    StockLockLogService stockLockLogService;

    @Autowired
    WarehouseMapper warehouseMapper;

    /**
     * @deprecated  单个加锁方法即将废弃，请使用批量加锁方法
     */
    @Override
    @Deprecated
    public String lock(LockBusType busType, Serializable busValue, String warehouseId,
                       String uc, UniqueCodeType ucType, String lockComment) {
        String materialId;
        // 先根据唯一码找到库存记录
        Stock stock = stockService.getOne(new QueryWrapper<Stock>()
                .eq(ucType.toString().toLowerCase(), uc)
                .eq("warehouse_id", warehouseId));
        CommonService.checkStock(stock,busType);
        // 判断库存是否有该唯一码
        if (stock == null) {
            throw new BusinessException("唯一码在该仓库未找到：" + uc);
        }
        if (stock.getSkuQty() <= 0) {
            throw new BusinessException("唯一码当前库存为%.2f", stock.getSkuQty());
        }

        // 判断是否已经被锁定
        StockLock stockLock = this.getOne(new QueryWrapper<StockLock>().eq("stock_id", stock.getId()));
        if (stockLock != null) {
            throw new BusinessException("唯一码已被锁定，锁定源信息：" + stockLock.getLockComment());
        }


        materialId = stock.getMatrId();

        // 加入锁定记录
        stockLock = new StockLock();
        stockLock.setWarehouseId(warehouseId);
        stockLock.setBusType(busType.toString());
        stockLock.setBusKeyValue((String) busValue);
        stockLock.setMatrId(materialId);
        stockLock.setStockId(stock.getId());
        stockLock.setUc(uc);
        stockLock.setUcType(ucType.toString());
        stockLock.setQty(stock.getSkuQty());
        stockLock.setLockTime(new Date());
        stockLock.setLockComment(lockComment);
        this.save(stockLock);
        CommonService.updateMatrStockNums(stockLock.getMatrId(),stockLock.getWarehouseId());
        return stock.getId();
    }

    @Override
    public List<String> lock(LockBusType busType, Serializable busValue, String warehouseId,
                             String materialId, Double qty, String lot, String lockComment) {
        // 判断是否已经锁定过，还未解锁
        List<StockLock> stockLockList = this.list(
                new QueryWrapper<StockLock>()
                        .eq("bus_type", busType.toString().toLowerCase())
                        .eq("bus_key_value", busValue)
                        .eq("warehouse_id", warehouseId));
        if (stockLockList.size() != 0) {
            throw new BusinessException("在此仓库，该业务关键字尚有未解锁的锁定记录：" + stockLockList.size());
        }

        // 判断总量是否还有额度
        Double availableQty = stockService.getAvailableStock(warehouseId, materialId, StockLockType.BY_LOT);
        availableQty = availableQty == null ? 0.0 : availableQty;
        if (availableQty < qty) {
            throw new BusinessException("库存数量不足,可用库存总数：" + availableQty);
        }

        List<String> stockIds = new ArrayList<>();
        List<Stock> stockList = stockService.getAvailableStockList(warehouseId, materialId, lot);
        Double restQty = qty;
        for (int i = 0; i < stockList.size(); i++) {
            if (restQty == 0) {
                break;
            }
            StockLock stockLock = new StockLock();
            stockLock.setWarehouseId(warehouseId);
            stockLock.setBusType(busType.toString());
            stockLock.setBusKeyValue((String) busValue);
            stockLock.setMatrId(materialId);
            stockLock.setLockTime(new Date());
            stockLock.setLockComment(lockComment);
            if (restQty >= stockList.get(i).getSkuQty()) {
                stockLock.setStockId(stockList.get(i).getId());
                stockLock.setQty(stockList.get(i).getSkuQty());
                stockIds.add(stockList.get(i).getId());
                restQty -= stockList.get(i).getSkuQty();
            } else {
                String stockId = stockService.splitStock(stockList.get(i), restQty);
                stockLock.setStockId(stockId);
                stockLock.setQty(restQty);
                stockIds.add(stockId);
                restQty = 0.0;
            }
            this.save(stockLock);

        }
        CommonService.updateMatrStockNums(materialId,warehouseId);
        if (restQty != 0) {
            throw new BusinessException("锁定计算错误，未能按照统计的库存全额锁库！");
        }
        return stockIds;
    }

    @Override
    public String virtualLock(LockBusType busType, Serializable busValue, String warehouseId, String matrId, Double qty) {
        // 判断是否已经锁定过，还未解锁
        StockLock stockLock = getVirtualLock(busType, busValue, warehouseId);
        if (stockLock != null) {
            throw new BusinessException("在此仓库，该业务关键字尚有未解锁的锁定记录!");
        }

        // 判断总量是否还有额度
        Double availableStock = stockService.getAvailableStock(warehouseId, matrId, StockLockType.VIRTUAL);
        availableStock = availableStock == null ? 0.0 : availableStock;
        if (availableStock < qty) {
            throw new BusinessException("库存数量不足,可用库存数为：" + availableStock);
        }
        stockLock = new StockLock();
        stockLock.setWarehouseId(warehouseId);
        stockLock.setBusType(busType.toString());
        stockLock.setBusKeyValue((String) busValue);
        stockLock.setMatrId(matrId);
        stockLock.setQty(qty);
        stockLock.setLockTime(new Date());
        stockLock.setLockComment("virtual lock");
        this.save(stockLock);
        return stockLock.getId();
    }

    @Override
    public StockLock getVirtualLock(LockBusType busType, Serializable busValue, String warehouseId) {
        return this.getOne(
                new QueryWrapper<StockLock>()
                        .eq("bus_type", busType.toString().toLowerCase())
                        .eq("bus_key_value", busValue)
                        .eq("warehouse_id", warehouseId));
    }

    @Override
    public List <StockLock> getVirtualLockList(LockBusType busType, List<String> busValues, String warehouseId) {
        return this.list(
                new QueryWrapper<StockLock>()
                        .eq("bus_type", busType.toString().toLowerCase())
                        .in("bus_key_value", busValues)
                        .eq("warehouse_id", warehouseId));
    }

    @Override
    public void reduceVirtualLock(String stockLockId, Double qty) {
        StockLock stockLock = this.getById(stockLockId);
        _reduceVirtualLock(stockLock, qty);
    }

    @Override
    public void reduceVirtualLock(LockBusType busType, Serializable busValue, String warehouseId, Double qty) {
        StockLock stockLock = getVirtualLock(busType, busValue, warehouseId);
        _reduceVirtualLock(stockLock, qty);
    }

    @Override
    public void addVirtualLock(String stockLockId, Double qty) {
        StockLock stockLock = this.getById(stockLockId);
        this._addVirtualLock(stockLock, qty);
    }

    @Override
    public void addVirtualLock(LockBusType busType, Serializable busValue, String warehouseId, Double qty) {
        StockLock stockLock = getVirtualLock(busType, busValue, warehouseId);
        _addVirtualLock(stockLock, qty);
    }

    /**
     * @deprecated  单个解锁方法即将废弃，请使用批量解锁方法
     */
    @Override
    @Deprecated
    public void unlock(String stockId) {
        unlock(stockId, false);
    }

    @Override
    public void unlock(String stockId, Boolean flagMergeStock) {
        StockLock stockLock = this.getOne(
                new QueryWrapper<StockLock>().eq("stock_id", stockId));
        if (stockLock == null) {
            throw new BusinessException("未能检索到锁定信息！");
        }
        _unlock(stockLock);
        if (flagMergeStock) {
            stockService.mergeStock(stockId);
        }
    }

    @Override
    public void virtualUnlock(LockBusType busType, Serializable busValue, String warehouseId) {
        List<StockLock> stockLockList = this.list(
                new QueryWrapper<StockLock>()
                        .eq("bus_type", busType.toString().toLowerCase())
                        .eq("bus_key_value", busValue)
                        .eq("warehouse_id", warehouseId));
        if (stockLockList.size() == 0) {
            throw new BusinessException("未能检索到锁定信息！");
        }
        for (StockLock stockLock : stockLockList) {
            _unlock(stockLock);
        }
    }

    @Override
    public void virtualUnlockList(LockBusType busType, List<String> busValues, String warehouseId) {
        List<StockLock> stockLockList = this.list(
                new QueryWrapper<StockLock>()
                        .eq("bus_type", busType.toString().toLowerCase())
                        .in("bus_key_value", busValues)
                        .eq("warehouse_id", warehouseId));
        if (stockLockList.size() == 0) {
            return;
        }
        _unlockBatch(stockLockList);

    }

    @Override
    public void unlock(String uc, UniqueCodeType ucType, String warehouseId) {
        // 判断是否被锁定
        StockLock stockLock = this.getOne(
                new QueryWrapper<StockLock>()
                        .eq("uc_type", ucType.toString().toLowerCase())
                        .eq("uc", uc)
                        .eq("warehouse_id", warehouseId));
        if (stockLock == null) {
            throw new BusinessException("唯一码无锁定信息！");
        }
        _unlock(stockLock);
    }

    private void _reduceVirtualLock(StockLock stockLock, Double qty) {
        if (stockLock == null) {
            throw new BusinessException("未能检索到锁定记录！");
        }
        Double result = stockLock.getQty() - qty < 0 ? 0.0 : stockLock.getQty() - qty;
        stockLock.setQty(result);
        this.updateById(stockLock);
    }

    private void _addVirtualLock(StockLock stockLock, Double qty) {
        if (stockLock == null) {
            throw new BusinessException("未能检索到锁定记录！");
        }
        Double availableStock = stockService.getAvailableStock(stockLock.getWarehouseId(),
                stockLock.getMatrId(), StockLockType.VIRTUAL);
        availableStock = availableStock == null ? 0.0 : availableStock;
        if (availableStock < qty) {
            throw new BusinessException("库存数量不足,可用库存数为：" + availableStock);
        }
        stockLock.setQty(stockLock.getQty() + qty);
        this.updateById(stockLock);
    }

    private void _unlock(StockLock stockLock) {
        StockLockLog stockLockLog = new StockLockLog();
        BeanUtils.copyProperties(stockLock, stockLockLog);
        stockLockLog.setUnlockTime(new Date());
        stockLockLogService.save(stockLockLog);
        this.removeById(stockLock);
        CommonService.updateMatrStockNums(stockLockLog.getMatrId(),stockLock.getWarehouseId());
    }

    private void _unlockBatch(List <StockLock> stockLocks) {
        List<StockLockLog> stockLockLogs = new ArrayList<>();
        List<String> lockIds = new ArrayList<>();
        Map<LockMatrDTO, StockLock> matrWarehouseIdMap = new HashMap<>();
        stockLocks.forEach(stockLock -> {
            StockLockLog stockLockLog = new StockLockLog();
            LockMatrDTO lockMatrDTO = new LockMatrDTO();
            BeanUtils.copyProperties(stockLock, stockLockLog);
            stockLockLog.setUnlockTime(new Date());
            stockLockLogs.add(stockLockLog);
            lockIds.add(stockLock.getId());
            lockMatrDTO.setMatrId(stockLock.getMatrId());
            lockMatrDTO.setWarehouseId(stockLock.getWarehouseId());
            matrWarehouseIdMap.put(lockMatrDTO, stockLock);
        });
        stockLockLogService.saveOrUpdateBatch(stockLockLogs);
        this.removeCascadeByIds(lockIds);
        stockLocks.forEach(stockLock -> {

        });
        matrWarehouseIdMap.keySet().forEach(LockMatrDTO->{
            CommonService.updateMatrStockNums(LockMatrDTO.getMatrId(),LockMatrDTO.getWarehouseId());
        });
    }

    /**
    * @Description： 唯一码批量加锁，要么全部成功要么全部失败 此方法有并发问题:库存锁定表用stock_id作为唯一索引来防止并发操作时的数据不一致性
     * @param  busType          业务类型，取自枚举类 LockBusType 【必须】
     * @param  busValue         即锁定来源，一般为业务模块主键，如：配送拣货锁定，则传配送单主键【必须】
     * @param  warehouseId      仓库id，用于筛选库存【必须】
     * @param  lockComment      锁定描述信息【必须】
     * @param  snList           需要锁定的sn列表【必须】
     * @param  refBusType       来源业务单据类型【必须】
     * @throws  没有符合条件的库存记录， snList 数据重复，必须参数为null
     * @return 锁定的库存记录列表
    */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Stock> lockBatch(LockBusType busType, String busValue, String warehouseId, String lockComment, List<String> snList,String refBusType) {
        String msgPre = "批量锁定失败，原因：";
        if (busType == null) throw new BusinessException(msgPre+"LockBusType 为空");
        if (StringUtils.isBlank(busValue)) throw new BusinessException(msgPre+"busValue 为空");
        if (StringUtils.isBlank(warehouseId)) throw new BusinessException(msgPre+"warehouseId 为空");
        if (StringUtils.isBlank(lockComment)) throw new BusinessException(msgPre+"lockComment 为空");
        if (snList == null || snList.size() < 1) throw new BusinessException(msgPre+"snList 为空");

        //1、snList是否重复
        snList.stream().forEach(ob1 -> {
            long count = snList.stream().filter(ob2 -> ob2.equals(ob1)).count();
            if (count > 1) throw new BusinessException(msgPre+"唯一码【%s】输入了%s次", ob1, count);
        });

        //2、库存查找
        List<Stock> stocks =stockService.list(Wrappers.<Stock>query().in("sn",snList));
        if (stocks == null || stocks.size() < 1) throw new BusinessException(msgPre+"未找到相关库存");
        List<Stock> stocksNot = stocks.stream().filter(stock -> !stock.getWarehouseId().equals(warehouseId)).collect(Collectors.toList());
        if (stocksNot != null && stocksNot.size() > 0) {
            Warehouse warehouse = warehouseMapper.selectById(warehouseId);
            throw new BusinessException(msgPre+"该库存在【%s】，无法在【%s】进行锁定操作",stocksNot.get(0).getWarehouseName(),warehouse !=null ? warehouse.getName() : "仓库已被删除,id="+warehouseId);
        }


        //21、库存校验,有无库存记录--总数不一致肯定有问题：用前端传过来的sn列表和查询到的库存列表中的sn对比
        if (stocks.size() != snList.size()){
            List<String> snListNoExist = snList.stream().filter(ob1 -> stocks.stream().noneMatch(ob2 -> ob1.equals(ob2.getSn())))
                    .collect(Collectors.toList());
            throw new BusinessException(msgPre+"唯一码【%s】未找到相关库存记录", snListNoExist.get(0));
        }

        //21、库存校验,有无库存数量--总数不一致肯定有问题：查询到的库存列表中的单品数量应该是<0的吧，而且这个判断是不会执行的
        if (stocks.size() != snList.size()){
            List<Stock> stockNoQtyExist = stocks.stream().filter(obtmp-> obtmp.getSkuQty() >0).collect(Collectors.toList());
            if (stockNoQtyExist !=null && stockNoQtyExist.size() > 0)
            throw new BusinessException(msgPre+"唯一码【%s】库存记录不足", stockNoQtyExist.get(0).getSn());
        }

        // 数量化管理的耗材id
        List<String> matrIdListMagnum = matrService.listByIds(stocks.stream().map(Stock::getMatrId).collect(Collectors.toList()))
                .stream().filter(item -> item.getFlagMagnum() != null && item.getFlagMagnum()).map(Matr::getId).collect(Collectors.toList());


        //22、库存校验,有无锁定记录--根据查询到库存列表得到的库存id查询库存锁定表看有无锁定记录
        List<String> stockIdList = stocks.stream().filter(item -> !matrIdListMagnum.contains(item.getMatrId())).map(Stock::getId).collect(Collectors.toList());
        if (!stockIdList.isEmpty()) {
            List<StockLock> stockLockList = this.list(Wrappers.<StockLock>query().in("stock_id",stockIdList));
            if (stockLockList != null && stockLockList.size() > 0)
                throw new BusinessException(msgPre+"唯一码【%s】已被锁定，锁定源【%s】", stockLockList.get(0).getUc(),stockLockList.get(0).getLockComment());
        }


        //3、不同业务类型的库存校验
        switch (busType.name()){
            case "DIST": //配送拣货
                List<Stock> stockListTmp = stocks.stream().filter(obtmp-> obtmp.getFlagOmoc()).collect(Collectors.toList());
                if (stockListTmp != null && stockListTmp.size() > 0)
                    throw new BusinessException(msgPre+"唯一码【%s】为一物一码，不支持拣货", stockListTmp.get(0).getSn());
                break;
        }

        //4批量加锁
        List<StockLock> stockLockSave = new ArrayList<StockLock>();
        Date now = DateUtils.now();
        StockLock stockLock = null;
        Double lockQty = 0d;
        for(Stock stock : stocks) {
            stockLock = new StockLock();
            stockLock.setWarehouseId(warehouseId);
            stockLock.setBusType(busType.toString());
            stockLock.setBusKeyValue((String) busValue);
            stockLock.setMatrId(stock.getMatrId());
            stockLock.setStockId(stock.getId());//锁定的是对应库存的id
            stockLock.setUc(stock.getSn());//唯一码类型
            stockLock.setUcType(UniqueCodeType.SN.name());
            stockLock.setQty(stock.getSkuQty());
            stockLock.setLockTime(now);
            stockLock.setLockComment(lockComment);
            stockLock.setRefBusType(refBusType);

            // 数量化管理耗材
            if (matrIdListMagnum.contains(stock.getMatrId())) {
                lockQty = getLockQty(stock.getId());
                if (stock.getSkuQty() - lockQty == 0) {
                    throw  new BusinessException("库存不足，请刷新重试，标签: %s",stock.getSn());
                }
                stockLock.setQty(stock.getSkuQty() - lockQty);
            }
            stockLockSave.add(stockLock);
        }
        try {
            //批量保存库存锁定信息
            this.saveBatch(stockLockSave,stockLockSave.size());//库存锁定表用stock_id作为唯一索引来防止并发操作时的数据不一致性
        } catch (Exception e) {
            logger.error("snList：{} ,库存锁定时出现异常，异常信息：{}", snList, e);
            throw new BusinessException(msgPre+"数据库无法锁定库存");
        }

        //5 更新锁定数量
        stocks.stream().map(Stock::getMatrId).distinct().forEach(matrId -> {
            CommonService.updateMatrStockNums(matrId,warehouseId);
        });
        return stocks;
    }
    /**
     * @Description： 唯一码批量解锁，要么全部成功要么全部失败
     * @param  snList           需要锁定的sn列表【必须】
     * @return 解锁的库存记录列表
     * @throws  没有符合条件的库存记录，解锁数量跟实际锁定数量不一致， snList 数据重复，必须参数为null
     */
    @Override
    public List<Stock> unlockBatch(List<String> snList) {
        String msgPre = "批量解锁失败，原因：";
        if (snList == null || snList.size() < 1) throw new BusinessException(msgPre+"snList 为空");

        //1、snList是否重复
        snList.stream().forEach(ob1 -> {
            long count = snList.stream().filter(ob2 -> ob2.equals(ob1)).count();
            if (count > 1) throw new BusinessException(msgPre+"唯一码【%s】输入了%s次", ob1, count);
        });

        //2、库存查找
        List<Stock> stocks =stockService.list(Wrappers.<Stock>query().in("sn",snList));
        if (stocks == null || stocks.size() < 1) throw new BusinessException(msgPre+"未找到相关库存");
        //21、库存校验,有无库存记录
        if (stocks.size() != snList.size()){
            List<String> snListNoExist = snList.stream().filter(ob1 -> stocks.stream().noneMatch(ob2 -> ob1.equals(ob2.getSn())))
                    .collect(Collectors.toList());
            throw new BusinessException(msgPre+"唯一码【%s】未找到相关库存记录", snListNoExist.get(0));
        }

        //21、库存校验,有无库存数量
        if (stocks.size() != snList.size()){
            List<Stock> stockNoQtyExist = stocks.stream().filter(obtmp-> obtmp.getSkuQty() >0).collect(Collectors.toList());
            if (stockNoQtyExist !=null && stockNoQtyExist.size() > 0)
                throw new BusinessException(msgPre+"唯一码【%s】库存记录不足", stockNoQtyExist.get(0).getSn());
        }

        //22、库存校验,有无锁定记录
        List<String> stockIdList = stocks.stream().map(Stock::getId).collect(Collectors.toList());
        List<StockLock> stockLockList = this.list(Wrappers.<StockLock>query().in("stock_id",stockIdList));
        if (stockLockList == null || stockLockList.size() < 1 || stockLockList.size() !=snList.size() )
            throw new BusinessException(msgPre+"解锁数量【%s】与已锁定数量【%s】不匹配", snList.size(),stockLockList != null ? stockLockList.size() : 0);


        //4批量解锁
        List<StockLock> stockLockSave = new ArrayList<StockLock>();
        List<StockLockLog> stockLockLogSave = new ArrayList<StockLockLog>();
        Date now = DateUtils.now();
        StockLockLog stockLockLog =null;
        for(StockLock stockLock : stockLockList) {
            stockLockLog = new StockLockLog();
            BeanUtils.copyProperties(stockLock, stockLockLog);
            stockLockLog.setUnlockTime(now);
            stockLockLog.setId(null);
            stockLockLogSave.add(stockLockLog);
        }
        stockLockLogService.saveBatch(stockLockLogSave,stockLockLogSave.size());
        this.removeCascadeByIds(stockLockList.stream().map(StockLock::getId).collect(Collectors.toList()));

        //5 更新锁定数量
        UtilJava8.stockGroupForUnlockBatch(stocks).forEach(stockGroup -> {
            CommonService.updateMatrStockNums(stockGroup.getMatrId(),stockGroup.getWarehouseId());
        });
        return stocks;
    }
    /**
     * @Description： 唯一码批量解锁，要么全部成功要么全部失败
     * @param  snList           需要锁定的sn列表【必须】
     * @return 解锁的库存记录列表
     * @throws  没有符合条件的库存记录，解锁数量跟实际锁定数量不一致， snList 数据重复，必须参数为null
     */
    @Override
    public List<Stock> unlockBatchWithBusKeyValue(List<String> snList, String busKeyValue) {
        String msgPre = "批量解锁失败，原因：";
        if (snList == null || snList.size() < 1) throw new BusinessException(msgPre+"snList 为空");

        //1、snList是否重复
        snList.stream().forEach(ob1 -> {
            long count = snList.stream().filter(ob2 -> ob2.equals(ob1)).count();
            if (count > 1) throw new BusinessException(msgPre+"唯一码【%s】输入了%s次", ob1, count);
        });

        //2、库存查找
        List<Stock> stocks =stockService.list(Wrappers.<Stock>query().in("sn",snList));
        if (stocks == null || stocks.size() < 1) throw new BusinessException(msgPre+"未找到相关库存");
        //21、库存校验,有无库存记录
        if (stocks.size() != snList.size()){
            List<String> snListNoExist = snList.stream().filter(ob1 -> stocks.stream().noneMatch(ob2 -> ob1.equals(ob2.getSn())))
                    .collect(Collectors.toList());
            throw new BusinessException(msgPre+"唯一码【%s】未找到相关库存记录", snListNoExist.get(0));
        }

        //21、库存校验,有无库存数量
        if (stocks.size() != snList.size()){
            List<Stock> stockNoQtyExist = stocks.stream().filter(obtmp-> obtmp.getSkuQty() >0).collect(Collectors.toList());
            if (stockNoQtyExist !=null && stockNoQtyExist.size() > 0)
                throw new BusinessException(msgPre+"唯一码【%s】库存记录不足", stockNoQtyExist.get(0).getSn());
        }

        //22、库存校验,有无锁定记录
        List<String> stockIdList = stocks.stream().map(Stock::getId).collect(Collectors.toList());
        List<StockLock> stockLockList = this.list(Wrappers.<StockLock>query().in("stock_id",stockIdList).eq("bus_key_value", busKeyValue));
        if (stockLockList == null || stockLockList.size() < 1 || stockLockList.size() !=snList.size() )
            throw new BusinessException(msgPre+"解锁数量【%s】与已锁定数量【%s】不匹配", snList.size(),stockLockList != null ? stockLockList.size() : 0);


        //4批量解锁
        List<StockLock> stockLockSave = new ArrayList<StockLock>();
        List<StockLockLog> stockLockLogSave = new ArrayList<StockLockLog>();
        Date now = DateUtils.now();
        StockLockLog stockLockLog =null;
        for(StockLock stockLock : stockLockList) {
            stockLockLog = new StockLockLog();
            BeanUtils.copyProperties(stockLock, stockLockLog);
            stockLockLog.setUnlockTime(now);
            stockLockLog.setId(null);
            stockLockLogSave.add(stockLockLog);
        }
        stockLockLogService.saveBatch(stockLockLogSave,stockLockLogSave.size());
        this.removeCascadeByIds(stockLockList.stream().map(StockLock::getId).collect(Collectors.toList()));

        //5 更新锁定数量
        UtilJava8.stockGroupForUnlockBatch(stocks).forEach(stockGroup -> {
            CommonService.updateMatrStockNums(stockGroup.getMatrId(),stockGroup.getWarehouseId());
        });
        return stocks;
    }

    /**
     * @description: 更新数量锁
     * @author: Tai
     * @date: 2025/3/25 12:08
     **/
    @Override
    public void updateLockQty(String stockId, String busKeyValue, Double lockQty) {
        Stock stock = stockService.getById(stockId);
        if (lockQty > stock.getSkuQty()) {
            throw  new BusinessException("锁定数量已超过最大单品数量【%s】",stock.getSkuQty());
        }
        StockLock stockLock = this.getOne(Wrappers.<StockLock>query().eq("stock_id",stockId).eq("bus_key_value",busKeyValue));
        if (stockLock == null) {
            throw  new BusinessException("未找到相关锁定记录");
        }
        stockLock.setQty(lockQty).setLockTime(new Date());
        this.updateById(stockLock);
    }

    // 获取某个库存已锁定数量
    @Override
    public Double getLockQty(String stockId) {
        return  this.list(Wrappers.<StockLock>query().eq("stock_id",stockId)).stream().mapToDouble(StockLock::getQty).sum();
    }
}
